# -*- coding: utf-8 -*-
"""
Created on Fri May  7 07:56:44 2021

@author: 雁痕
"""

import argparse
import logging
import pickle
import time
import traceback
from redislite import Redis
from wrenchbox.logging import setup_log
from blockchain import Block
from ledger import GENESIS

DEFAULTS = {'min_timespan': 180}

class Builder:
    def __init__(self, url):
        self.db = Redis(url)

    def run(self, k_size, cool_down):
        while True:
            pool = pickle.loads(self.db.get('pool'))
            """if len(pool) >= k_size:"""
            if len(pool):
                t_min = min([i.t for i in pool])
                t_max = max([i.t for i in pool])
                if t_max - t_min >= DEFAULTS['min_timespan']:
                    self.db.set('pool', pickle.dumps(set([])))
                    logging.info('Pool is cleared.')
                    logging.info('Packing %d transactions...', len(pool))
                
                    ledger = pickle.loads(self.db.get('ledger'))
                    block = Block()
                    for transaction in pool:
                        if transaction.sender == GENESIS \
                            or ledger.balance(transaction.sender) >= transaction.amount:
                                block.add(transaction)
                        else:
                            logging.warning('Dropped transaction due to not enough balance: %s', transaction)
                    logging.info('Block is created with %d records.', len(block.items))
                
                if len(block.items):
                    try:
                        block.validate()
                    except AssertionError:
                        logging.error('Block is invalid and dropped.')
                        if args.debug:
                            traceback.print_exc()
                    else:
                        ledger.add(block)
                        try:
                            ledger.validate()
                        except AssertionError:
                            logging.error('Ledger is invalid and dropped.')
                            if args.debug:
                                traceback.print_exc()
                        else:
                            self.db.set('ledger',pickle.dumps(ledger))
                            logging.info('Block is added to the blockchain.')

            else:
                logging.debug('Currently %d records, require: %d', len(pool), k_size)
            time.sleep(cool_down)
            
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--debug',
        action='store_true', default=False,
        help='show debug information'
        )
    parser.add_argument(
        '-r','--redis',
        type=str, default='redis.db',
        help='redis database file, default: redis.db'
        )
    parser.add_argument(
        '-k','--size',
        type=int, default=3,
        help='# of minimum packed transactions, default: 3'
        )
    parser.add_argument(
        '-t','--sleep',
        type=int, default=3,
        help='refresh rate in seconds, default: 3'
        )

    args, _ = parser.parse_known_args()
    setup_log(level=logging.DEBUG if args.debug else logging.INFO)
    Builder(args.redis).run(args.size, args.sleep)
                
                